/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.ior.iiop;

import java.util.List;
import java.util.Iterator;

import org.omg.CORBA.SystemException;

import org.omg.CORBA_2_3.portable.OutputStream;
import org.omg.CORBA_2_3.portable.InputStream;

import org.omg.IOP.TAG_ALTERNATE_IIOP_ADDRESS;
import org.omg.IOP.TAG_INTERNET_IOP;
import org.omg.IOP.TAG_JAVA_CODEBASE;

import com.sun.corba.se.spi.protocol.RequestDispatcherRegistry;

import com.sun.corba.se.spi.oa.ObjectAdapter;
import com.sun.corba.se.spi.oa.ObjectAdapterFactory;

import com.sun.corba.se.spi.ior.ObjectId;
import com.sun.corba.se.spi.ior.ObjectAdapterId;
import com.sun.corba.se.spi.ior.TaggedProfile;
import com.sun.corba.se.spi.ior.TaggedProfileTemplate;
import com.sun.corba.se.spi.ior.ObjectKey;
import com.sun.corba.se.spi.ior.ObjectKeyTemplate;
import com.sun.corba.se.spi.ior.TaggedComponent;
import com.sun.corba.se.spi.ior.IdentifiableBase;
import com.sun.corba.se.spi.ior.IORFactories;
import com.sun.corba.se.spi.ior.ObjectKeyFactory;

import com.sun.corba.se.spi.ior.iiop.IIOPAddress;
import com.sun.corba.se.spi.ior.iiop.IIOPProfile;
import com.sun.corba.se.spi.ior.iiop.IIOPProfileTemplate;
import com.sun.corba.se.spi.ior.iiop.IIOPFactories;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.ior.iiop.JavaCodebaseComponent;

import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.orb.ORBVersion;

import com.sun.corba.se.spi.logging.CORBALogDomains;

import com.sun.corba.se.impl.ior.EncapsulationUtility;

import com.sun.corba.se.impl.encoding.EncapsInputStream;
import com.sun.corba.se.impl.encoding.EncapsOutputStream;

import sun.corba.EncapsInputStreamFactory;

import com.sun.corba.se.impl.util.JDKBridge;

import com.sun.corba.se.impl.logging.IORSystemException;

/**
 * @author
 */
public class IIOPProfileImpl extends IdentifiableBase implements IIOPProfile {
    private ORB orb;
    private IORSystemException wrapper;
    private ObjectId oid;
    private IIOPProfileTemplate proftemp;
    private ObjectKeyTemplate oktemp;

    // Cached lookups
    protected String codebase = null;
    protected boolean cachedCodebase = false;

    private boolean checkedIsLocal = false;
    private boolean cachedIsLocal = false;

    // initialize-on-demand holder
    private static class LocalCodeBaseSingletonHolder {
        public static JavaCodebaseComponent comp;

        static {
            String localCodebase = JDKBridge.getLocalCodebase();
            if (localCodebase == null)
                comp = null;
            else
                comp = IIOPFactories.makeJavaCodebaseComponent(localCodebase);
        }
    }

    private GIOPVersion giopVersion = null;

    public boolean equals(Object obj) {
        if (!(obj instanceof IIOPProfileImpl))
            return false;

        IIOPProfileImpl other = (IIOPProfileImpl) obj;

        return oid.equals(other.oid) && proftemp.equals(other.proftemp) && oktemp.equals(other.oktemp);
    }

    public int hashCode() {
        return oid.hashCode() ^ proftemp.hashCode() ^ oktemp.hashCode();
    }

    public ObjectId getObjectId() {
        return oid;
    }

    public TaggedProfileTemplate getTaggedProfileTemplate() {
        return proftemp;
    }

    public ObjectKeyTemplate getObjectKeyTemplate() {
        return oktemp;
    }

    private IIOPProfileImpl(ORB orb) {
        this.orb = orb;
        wrapper = IORSystemException.get(orb, CORBALogDomains.OA_IOR);
    }

    public IIOPProfileImpl(ORB orb, ObjectKeyTemplate oktemp, ObjectId oid, IIOPProfileTemplate proftemp) {
        this(orb);
        this.oktemp = oktemp;
        this.oid = oid;
        this.proftemp = proftemp;
    }

    public IIOPProfileImpl(InputStream is) {
        this((ORB) (is.orb()));
        init(is);
    }

    public IIOPProfileImpl(ORB orb, org.omg.IOP.TaggedProfile profile) {
        this(orb);

        if (profile == null || profile.tag != TAG_INTERNET_IOP.value || profile.profile_data == null) {
            throw wrapper.invalidTaggedProfile();
        }

        EncapsInputStream istr = EncapsInputStreamFactory.newEncapsInputStream((ORB) orb, profile.profile_data, profile.profile_data.length);
        istr.consumeEndian();
        init(istr);
    }

    private void init(InputStream istr) {
        // First, read all of the IIOP IOR data
        GIOPVersion version = new GIOPVersion();
        version.read(istr);
        IIOPAddress primary = new IIOPAddressImpl(istr);
        byte[] key = EncapsulationUtility.readOctets(istr);

        ObjectKey okey = orb.getObjectKeyFactory().create(key);
        oktemp = okey.getTemplate();
        oid = okey.getId();

        proftemp = IIOPFactories.makeIIOPProfileTemplate(orb, version, primary);

        // Handle any tagged components (if applicable)
        if (version.getMinor() > 0)
            EncapsulationUtility.readIdentifiableSequence(proftemp, orb.getTaggedComponentFactoryFinder(), istr);

        // If there is no codebase in this IOR and there IS a
        // java.rmi.server.codebase property set, we need to
        // update the IOR with the local codebase.  Note that
        // there is only one instance of the local codebase, but it
        // can be safely shared in multiple IORs since it is immutable.
        if (uncachedGetCodeBase() == null) {
            JavaCodebaseComponent jcc = LocalCodeBaseSingletonHolder.comp;

            if (jcc != null) {
                if (version.getMinor() > 0)
                    proftemp.add(jcc);

                codebase = jcc.getURLs();
            }

            // Whether codebase is null or not, we have it,
            // and so getCodebase ned never call uncachedGetCodebase.
            cachedCodebase = true;
        }
    }

    public void writeContents(OutputStream os) {
        proftemp.write(oktemp, oid, os);
    }

    public int getId() {
        return proftemp.getId();
    }

    public boolean isEquivalent(TaggedProfile prof) {
        if (!(prof instanceof IIOPProfile))
            return false;

        IIOPProfile other = (IIOPProfile) prof;

        return oid.equals(other.getObjectId()) && proftemp.isEquivalent(other.getTaggedProfileTemplate()) && oktemp.equals(other.getObjectKeyTemplate());
    }

    public ObjectKey getObjectKey() {
        ObjectKey result = IORFactories.makeObjectKey(oktemp, oid);
        return result;
    }

    public org.omg.IOP.TaggedProfile getIOPProfile() {
        EncapsOutputStream os = sun.corba.OutputStreamFactory.newEncapsOutputStream(orb);
        os.write_long(getId());
        write(os);
        InputStream is = (InputStream) (os.create_input_stream());
        return org.omg.IOP.TaggedProfileHelper.read(is);
    }

    private String uncachedGetCodeBase() {
        Iterator iter = proftemp.iteratorById(TAG_JAVA_CODEBASE.value);

        if (iter.hasNext()) {
            JavaCodebaseComponent jcbc = (JavaCodebaseComponent) (iter.next());
            return jcbc.getURLs();
        }

        return null;
    }

    public synchronized String getCodebase() {
        if (!cachedCodebase) {
            cachedCodebase = true;
            codebase = uncachedGetCodeBase();
        }

        return codebase;
    }

    /**
     * @return the ORBVersion associated with the object key in the IOR.
     */
    public ORBVersion getORBVersion() {
        return oktemp.getORBVersion();
    }

    public synchronized boolean isLocal() {
        if (!checkedIsLocal) {
            checkedIsLocal = true;
            String host = proftemp.getPrimaryAddress().getHost();

            cachedIsLocal = orb.isLocalHost(host) && orb.isLocalServerId(oktemp.getSubcontractId(), oktemp.getServerId()) && orb.getLegacyServerSocketManager().legacyIsLocalServerPort(proftemp.getPrimaryAddress().getPort());
        }

        return cachedIsLocal;
    }

    /** Return the servant for this IOR, if it is local AND if the OA that
     * implements this objref supports direct access to servants outside of an
     * invocation.
     * XXX revisit: do we want this at all?  If we do, it might move to the
     * ObjectKeyTemplate instead.
     */
    public java.lang.Object getServant() {
        if (!isLocal())
            return null;

        RequestDispatcherRegistry scr = orb.getRequestDispatcherRegistry();
        ObjectAdapterFactory oaf = scr.getObjectAdapterFactory(oktemp.getSubcontractId());

        ObjectAdapterId oaid = oktemp.getObjectAdapterId();
        ObjectAdapter oa = null;

        try {
            oa = oaf.find(oaid);
        } catch (SystemException exc) {
            // Could not find the OA, so just return null.
            // This usually happens when POAs are being deleted,
            // and the POA always return null for getLocalServant anyway.
            wrapper.getLocalServantFailure(exc, oaid.toString());
            return null;
        }

        byte[] boid = oid.getId();
        java.lang.Object servant = oa.getLocalServant(boid);
        return servant;
    }

    /**
     * Return GIOPVersion for this IOR.
     * Requests created against this IOR will be of the
     * return Version.
     */
    public synchronized GIOPVersion getGIOPVersion() {
        return proftemp.getGIOPVersion();
    }

    public void makeImmutable() {
        proftemp.makeImmutable();
    }
}
